//@version=6
// Auto Fib Extension Targets A-B-C
// Projects Fibonacci extension targets from the latest validated A-B-C swing
// Author: MartenBG

indicator("Auto Fib Extension Targets A-B-C by MartenBG", shorttitle="Fib Ext ABC - MartenBG", overlay=true, max_lines_count=500, max_labels_count=500)

// Inputs
grpCore = "Core"
leftBars   = input.int(5, "Pivot left",  minval=1, group=grpCore)
rightBars  = input.int(5, "Pivot right", minval=1, group=grpCore)
mode       = input.string("Both", "Mode", options=["Up", "Down", "Both"], group=grpCore)
extendBars = input.int(80, "Horizontal extension in bars", minval=10, step=10, group=grpCore)

// Mode
bool showLong  = mode != "Down"
bool showShort = mode != "Up"

grpValid = "Correction validation"
minRetr  = input.float(0.236, "Min retrace vs AB", step=0.001, group=grpValid)
maxRetr  = input.float(0.786, "Max retrace vs AB", step=0.001, group=grpValid)
needHL   = input.bool(true, "Require HL for uptrend and LH for downtrend", group=grpValid)

grpLvls = "Levels"
use100   = input.bool(true,  "1.000", group=grpLvls)
use1272  = input.bool(true,  "1.272", group=grpLvls)
use1382  = input.bool(true,  "1.382", group=grpLvls)
use1618  = input.bool(true,  "1.618", group=grpLvls)
use200   = input.bool(false, "2.000", group=grpLvls)
showZone = input.bool(true,  "Show 1.272 - 1.382 target zone", group=grpLvls)

plotAB   = input.bool(true, "Show A-B-C segments", group="Visuals")

// Pivots
var int[]   pIdx   = array.new_int()
var float[] pPrice = array.new_float()
var int[]   pType  = array.new_int()  // -1 low, 1 high

ph = ta.pivothigh(high, leftBars, rightBars)
pl = ta.pivotlow(low,  leftBars, rightBars)

// Store new pivot
if not na(ph)
    int idx = bar_index - rightBars
    int sz = array.size(pType)
    if sz > 0 and array.get(pType, sz - 1) == 1
        array.set(pIdx, sz - 1, idx)
        array.set(pPrice, sz - 1, ph)
    else
        array.push(pIdx, idx)
        array.push(pPrice, ph)
        array.push(pType, 1)
        if array.size(pIdx) > 20
            array.shift(pIdx)
            array.shift(pPrice)
            array.shift(pType)

if not na(pl)
    int idx2 = bar_index - rightBars
    int sz2 = array.size(pType)
    if sz2 > 0 and array.get(pType, sz2 - 1) == -1
        array.set(pIdx, sz2 - 1, idx2)
        array.set(pPrice, sz2 - 1, pl)
    else
        array.push(pIdx, idx2)
        array.push(pPrice, pl)
        array.push(pType, -1)
        if array.size(pIdx) > 20
            array.shift(pIdx)
            array.shift(pPrice)
            array.shift(pType)

// Latest valid A-B-C containers
var int aU = na
var int bU = na
var int cU = na
var float aUp = na
var float bUp = na
var float cUp = na

var int aD = na
var int bD = na
var int cD = na
var float aDp = na
var float bDp = na
var float cDp = na

// Find latest valid A-B-C
if array.size(pType) >= 3
    int t2 = array.get(pType,  array.size(pType)  - 3)
    int t1 = array.get(pType,  array.size(pType)  - 2)
    int t0 = array.get(pType,  array.size(pType)  - 1)
    int iA = array.get(pIdx,   array.size(pIdx)   - 3)
    int iB = array.get(pIdx,   array.size(pIdx)   - 2)
    int iC = array.get(pIdx,   array.size(pIdx)   - 1)
    float pA = array.get(pPrice, array.size(pPrice) - 3)
    float pB = array.get(pPrice, array.size(pPrice) - 2)
    float pC = array.get(pPrice, array.size(pPrice) - 1)

    // Uptrend L - H - L
    if t2 == -1 and t1 == 1 and t0 == -1
        float ab = pB - pA
        float retr = ab != 0.0 ? (pB - pC) / ab : na
        bool passRetr = not na(retr) and retr >= minRetr and retr <= maxRetr
        bool passHL = not needHL or pC > pA
        if passRetr and passHL
            aU := iA
            bU := iB
            cU := iC
            aUp := pA
            bUp := pB
            cUp := pC

    // Downtrend H - L - H
    if t2 == 1 and t1 == -1 and t0 == 1
        float abD = pA - pB
        float retrD = abD != 0.0 ? (pC - pB) / abD : na
        bool passRetrD = not na(retrD) and retrD >= minRetr and retrD <= maxRetr
        bool passLH = not needHL or pC < pA
        if passRetrD and passLH
            aD := iA
            bD := iB
            cD := iC
            aDp := pA
            bDp := pB
            cDp := pC

// Drawing containers
var line[] longLines  = array.new_line()
var label[] longLabs  = array.new_label()
var line[] shortLines = array.new_line()
var label[] shortLabs = array.new_label()
var box longBox = na
var box shortBox = na
var line upAB = na
var line upBC = na
var line dnAB = na
var line dnBC = na

// Safe cleanup
if not barstate.isfirst
    int ll = array.size(longLines)
    if ll > 0
        for i = 0 to ll - 1
            line.delete(array.get(longLines, i))
    array.clear(longLines)

    int lla = array.size(longLabs)
    if lla > 0
        for i = 0 to lla - 1
            label.delete(array.get(longLabs, i))
    array.clear(longLabs)

    int sl = array.size(shortLines)
    if sl > 0
        for i = 0 to sl - 1
            line.delete(array.get(shortLines, i))
    array.clear(shortLines)

    int sla = array.size(shortLabs)
    if sla > 0
        for i = 0 to sla - 1
            label.delete(array.get(shortLabs, i))
    array.clear(shortLabs)

    if not na(longBox)
        box.delete(longBox)
        longBox := na
    if not na(shortBox)
        box.delete(shortBox)
        shortBox := na

// Long targets - width set to 1
if not barstate.isfirst and showLong and not na(aU) and not na(bU) and not na(cU)
    float abUp = bUp - aUp
    int x1u = cU
    int x2u = bar_index + extendBars

    if use100
        float y_u100 = cUp + 1.0 * abUp
        line ln_u100 = line.new(x1u, y_u100, x2u, y_u100, xloc=xloc.bar_index, extend=extend.none, color=color.teal, width=1)
        label lb_u100 = label.new(x2u, y_u100, "1.000  " + str.tostring(y_u100, format.mintick), xloc=xloc.bar_index, textcolor=color.white, style=label.style_label_right, color=color.teal)
        array.push(longLines, ln_u100)
        array.push(longLabs, lb_u100)
    if use1272
        float y_u1272 = cUp + 1.272 * abUp
        line ln_u1272 = line.new(x1u, y_u1272, x2u, y_u1272, xloc=xloc.bar_index, extend=extend.none, color=color.orange, width=1)
        label lb_u1272 = label.new(x2u, y_u1272, "1.272  " + str.tostring(y_u1272, format.mintick), xloc=xloc.bar_index, textcolor=color.white, style=label.style_label_right, color=color.orange)
        array.push(longLines, ln_u1272)
        array.push(longLabs, lb_u1272)
    if use1382
        float y_u1382 = cUp + 1.382 * abUp
        line ln_u1382 = line.new(x1u, y_u1382, x2u, y_u1382, xloc=xloc.bar_index, extend=extend.none, color=color.yellow, width=1)
        label lb_u1382 = label.new(x2u, y_u1382, "1.382  " + str.tostring(y_u1382, format.mintick), xloc=xloc.bar_index, textcolor=color.white, style=label.style_label_right, color=color.yellow)
        array.push(longLines, ln_u1382)
        array.push(longLabs, lb_u1382)
    if use1618
        float y_u1618 = cUp + 1.618 * abUp
        line ln_u1618 = line.new(x1u, y_u1618, x2u, y_u1618, xloc=xloc.bar_index, extend=extend.none, color=color.purple, width=1)
        label lb_u1618 = label.new(x2u, y_u1618, "1.618  " + str.tostring(y_u1618, format.mintick), xloc=xloc.bar_index, textcolor=color.white, style=label.style_label_right, color=color.purple)
        array.push(longLines, ln_u1618)
        array.push(longLabs, lb_u1618)
    if use200
        float y_u200 = cUp + 2.0 * abUp
        line ln_u200 = line.new(x1u, y_u200, x2u, y_u200, xloc=xloc.bar_index, extend=extend.none, color=color.fuchsia, width=1)
        label lb_u200 = label.new(x2u, y_u200, "2.000  " + str.tostring(y_u200, format.mintick), xloc=xloc.bar_index, textcolor=color.white, style=label.style_label_right, color=color.fuchsia)
        array.push(longLines, ln_u200)
        array.push(longLabs, lb_u200)

    if showZone
        float zTop = cUp + 1.382 * abUp
        float zBot = cUp + 1.272 * abUp
        float y1 = math.max(zTop, zBot)
        float y2 = math.min(zTop, zBot)
        longBox := box.new(x1u, y1, x2u, y2, xloc=xloc.bar_index, bgcolor=color.new(color.yellow, 85), border_color=color.yellow)

// Short targets - width set to 1
if not barstate.isfirst and showShort and not na(aD) and not na(bD) and not na(cD)
    float abDn = aDp - bDp
    int x1d = cD
    int x2d = bar_index + extendBars

    if use100
        float y_d100 = cDp - 1.0 * abDn
        line ln_d100 = line.new(x1d, y_d100, x2d, y_d100, xloc=xloc.bar_index, extend=extend.none, color=color.teal, width=1)
        label lb_d100 = label.new(x2d, y_d100, "1.000  " + str.tostring(y_d100, format.mintick), xloc=xloc.bar_index, textcolor=color.white, style=label.style_label_right, color=color.teal)
        array.push(shortLines, ln_d100)
        array.push(shortLabs, lb_d100)
    if use1272
        float y_d1272 = cDp - 1.272 * abDn
        line ln_d1272 = line.new(x1d, y_d1272, x2d, y_d1272, xloc=xloc.bar_index, extend=extend.none, color=color.orange, width=1)
        label lb_d1272 = label.new(x2d, y_d1272, "1.272  " + str.tostring(y_d1272, format.mintick), xloc=xloc.bar_index, textcolor=color.white, style=label.style_label_right, color=color.orange)
        array.push(shortLines, ln_d1272)
        array.push(shortLabs, lb_d1272)
    if use1382
        float y_d1382 = cDp - 1.382 * abDn
        line ln_d1382 = line.new(x1d, y_d1382, x2d, y_d1382, xloc=xloc.bar_index, extend=extend.none, color=color.yellow, width=1)
        label lb_d1382 = label.new(x2d, y_d1382, "1.382  " + str.tostring(y_d1382, format.mintick), xloc=xloc.bar_index, textcolor=color.white, style=label.style_label_right, color=color.yellow)
        array.push(shortLines, ln_d1382)
        array.push(shortLabs, lb_d1382)
    if use1618
        float y_d1618 = cDp - 1.618 * abDn
        line ln_d1618 = line.new(x1d, y_d1618, x2d, y_d1618, xloc=xloc.bar_index, extend=extend.none, color=color.purple, width=1)
        label lb_d1618 = label.new(x2d, y_d1618, "1.618  " + str.tostring(y_d1618, format.mintick), xloc=xloc.bar_index, textcolor=color.white, style=label.style_label_right, color=color.purple)
        array.push(shortLines, ln_d1618)
        array.push(shortLabs, lb_d1618)
    if use200
        float y_d200 = cDp - 2.0 * abDn
        line ln_d200 = line.new(x1d, y_d200, x2d, y_d200, xloc=xloc.bar_index, extend=extend.none, color=color.fuchsia, width=1)
        label lb_d200 = label.new(x2d, y_d200, "2.000  " + str.tostring(y_d200, format.mintick), xloc=xloc.bar_index, textcolor=color.white, style=label.style_label_right, color=color.fuchsia)
        array.push(shortLines, ln_d200)
        array.push(shortLabs, lb_d200)

    if showZone
        float zTop_d = cDp - 1.272 * abDn
        float zBot_d = cDp - 1.382 * abDn
        float y1_d = math.max(zTop_d, zBot_d)
        float y2_d = math.min(zTop_d, zBot_d)
        shortBox := box.new(x1d, y1_d, x2d, y2_d, xloc=xloc.bar_index, bgcolor=color.new(color.purple, 85), border_color=color.purple)

// Visual A-B-C helpers (unchanged width for dotted segments)
if not barstate.isfirst and plotAB
    if not na(upAB)
        line.delete(upAB)
    if not na(upBC)
        line.delete(upBC)
    if not na(dnAB)
        line.delete(dnAB)
    if not na(dnBC)
        line.delete(dnBC)

    if not na(aU) and not na(bU)
        upAB := line.new(aU, aUp, bU, bUp, xloc=xloc.bar_index, extend=extend.none, color=color.new(color.teal, 40), width=2, style=line.style_dotted)
    if not na(bU) and not na(cU)
        upBC := line.new(bU, bUp, cU, cUp, xloc=xloc.bar_index, extend=extend.none, color=color.new(color.teal, 40), width=2, style=line.style_dotted)
    if not na(aD) and not na(bD)
        dnAB := line.new(aD, aDp, bD, bDp, xloc=xloc.bar_index, extend=extend.none, color=color.new(color.purple, 40), width=2, style=line.style_dotted)
    if not na(bD) and not na(cD)
        dnBC := line.new(bD, bDp, cD, cDp, xloc=xloc.bar_index, extend=extend.none, color=color.new(color.purple, 40), width=2, style=line.style_dotted)
